{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 函数也是数据：初级篇"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "前面我们已经提过一次，**函数也是一种对象**。\n",
    "\n",
    "或者说：**函数也是数据**（*function is data*），是差不多的。\n",
    "\n",
    "这是个重要的时刻，从现在开始我们对函数的理解将有一个新的飞跃。函数虽然是操作数据的工具，但它自己也是个数据对象，我们可以对它进行各种各样的操作，甚至在运行时动态的构造出一个函数来，这些都是 Python（以及很多流行的现代化编程语言）的亮点。我们在这一部分的最后还会就这个话题进行展开，从而引入一系列非常有用的“**函数式**（*functional*）”编程工具，在目前这一章我们先稍微品尝一下这个概念，来看看函数别名和匿名函数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 函数别名"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在 Python 中，所有函数也是对象，证据就是它们都有对象 id。Python 会为创建的每一个对象（不管基本数据类型，还是某个 *class* 的实例）指定一个唯一的 id，可以用内置函数 `id()` 来查看，比如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4356419792"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n = 42\n",
    "id(n)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "函数也有这个 id，比如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def _is_leap(year):\n",
    "    return (year % 4 == 0 and year % 100 != 0) or year % 400 == 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4394147424"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id(_is_leap)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "既然函数有 id，是个对象，那是什么类型的对象呢？可以用内置函数 `type` 来看："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "function"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(_is_leap)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "所以函数是个 `function` 类型的对象。\n",
    "\n",
    "既然是个对象，我们就可以用赋值语句来创建函数的**别名**（*alias*）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "is_leap = _is_leap"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4394147424"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id(is_leap)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看到，这两个函数的 id 完全一样，是同一个对象的两个名字而已。我们可以用这两个名字来调用这个函数，完全没区别："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "_is_leap(2018)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "is_leap(2018)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "那么，我们为什么需要给函数取别名呢？\n",
    "\n",
    "很多时候是为了提供更好的代码可读性，比如在特定上下文让某个函数的作用更显而易见，比如以前的例子里，我们曾经在 `Cat` 类里给父类 `Animal` 的 `voice()` 方法定义别名叫 `meow()`。\n",
    "\n",
    "还有一种情况是一个函数需要在运行时动态指向不同的实现版本。这里只简单描述一个典型场景：假定我们要渲染一段视频，如果系统里有兼容的显卡（GPU），就调用显卡来渲染，会更快更省电，如果没有则用 CPU 来渲染，会慢一点和更耗电一点，于是我们把用 GPU 渲染的算法写成函数 `_render_by_gpu()`，用 CPU 渲染的算法写成函数 `_render_by_cpu()`，而检测是否存在可用 GPU 的算法写成函数 `is_gpu_available()`，然后可以用下面的方法来定义一个函数 `render`：\n",
    "\n",
    "```python\n",
    "if is_gpu_available():\n",
    "    render = _render_by_gpu\n",
    "else:\n",
    "    render = _render_by_cpu\n",
    "```\n",
    "\n",
    "这样 `render()` 就成为一个当前系统中最优化的渲染函数，在程序的其他地方就不用管细节，直接用这个函数就好。这就是动态函数别名的价值。\n",
    "\n",
    "顺便说一句，在任何一个工程里，为函数或者变量取名都是**很简单却不容易**的事情——因为可能会重名（虽然已经尽量用变量的作用域隔离了），可能会因取名含混而令后来者费解，所以，仅仅为了少敲几下键盘而给一个函数取个更短的别名，实际上并不是好主意，更不是好习惯。尤其现在的编辑器都支持自动补全和多光标编辑的功能，变量名长点不是什么大问题。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 匿名函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有的函数需要两个甚至更多名字，有的函数却一个也不要，人生就是这么丰富多彩啊！\n",
    "\n",
    "所谓匿名函数，就是有时候我们需要一个函数，但就在一个地方，用完就扔，再也不会用了，Python 对这种情况提供了一个方便的语法，不需要 `def` 那套严肃完整的语法，一行就可以写完一个函数，这个语法使用关键字 `lambda`。`lambda` 是希腊字母 `λ` 的英语音译，在计算机领域是个来头不小的词儿，代表了一系列高深的理论，[和阿伦佐·丘奇（Alonzo Church）的理论有关](https://en.wikipedia.org/wiki/Lambda_calculus)，有兴趣的话可以自行研究。\n",
    "\n",
    "不过目前我们不需要管那么多，只要了解怎么快速创建“用过即扔”的小函数就好了。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "比如下面这个很简单的函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def add(x, y):\n",
    "    return x + y\n",
    "add(3, 5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们可以用 `lambda` 来改写："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add = lambda x, y: x + y\n",
    "add(3, 5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "甚至更简单一点，名字也不要了："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(lambda x, y: x + y)(3, 5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后这种形式，就是典型的匿名函数了。简单地说，`lambda` 可以生成一个函数对象，出现在所有需要一个函数的地方，可以将其赋给一个变量（如上面的 `add`），这个变量就称为函数变量（别名），可以当函数用；也可以直接把 `lambda` 语句用括号括起来当一个函数用（上面后一种形式）。\n",
    "\n",
    "在 Python 官方文档中，`lambda` 语句的语法定义是这样的：\n",
    "\n",
    "`lambda_expr ::= \"lambda\" [parameter_list] \":\" expression`\n",
    "\n",
    "这个语法定义采用的是 [巴克斯范式（BNF）](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form)标注，现在不明白没关系（虽然对照上面的例子也能猜出个大概吧），以后我们会专门介绍。\n",
    "\n",
    "其实也很简单，就是这个样子：\n",
    "\n",
    "```python\n",
    "lambda x, y: x + y\n",
    "```\n",
    "\n",
    "先写上 `lambda` 关键字，其后分为两个部分，`:` 之前是参数表，之后是表达式，这个表达式的值，就是这个函数的返回值。**注意**：`lambda` 语句中，`:` 之后有且只能有一个表达式，所以它搞不出很复杂的函数，比较适合一句话的函数。\n",
    "\n",
    "而这个函数呢，没有名字，所以被称为 “匿名函数”。\n",
    "\n",
    "`add = lambda x, y: x + y`\n",
    "\n",
    "就相当于是给一个没有名字的函数取了个名字。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们在后面的学习中会遇到很多 `lambda` 的例子，因为有很多地方需要这种就用一次而且一句话的小函数，目前大家只要对这个概念和语法有所理解并且记住就可以了。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 小结"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* 函数也是对象，有 *id*，是 `function` 类型；\n",
    "* 所以函数也可以被赋值给一个变量，把那个变量变成自己的别名（*alias*）；\n",
    "* 可以用 `lambda` 来创建一次性、一句话的小函数，在很多场景下都很有用。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
